#ifndef __wUIKIT_H__
#define __wUIKIT_H__

#if !WINDOWS_SYSTEM
	#error only windows system supported
#endif

#ifndef __ETLBASE_H__
#error etluikit.h requires etlbase.h to be included first
#endif

#if !defined _MFC_VER && !defined __ATLMISC_H__
#error wuikit.h requires atlmisc.h to be included first
#endif

class wIniFile
{
public:
	BOOL OpenKey(LPCTSTR strFileName, LPCTSTR strSectionName)
	{
		m_strFileName = strFileName;
		m_strSectionName = strSectionName;
		return TRUE;
	}
	
	LPCTSTR LoadValue(LPCTSTR strValueName, LPCTSTR strDefValue)
	{
		chASSERT(!m_strFileName.IsEmpty());
		GetPrivateProfileString(m_strSectionName, strValueName, strDefValue, 
			m_strData, sizeof(m_strData) / sizeof(TCHAR), m_strFileName);
		return m_strData;
	}
	
	BOOL SaveValue(LPCTSTR strValueName, LPCTSTR strValueData)
	{
		chASSERT(!m_strFileName.IsEmpty());
		return WritePrivateProfileString(m_strSectionName, strValueName, strValueData, m_strFileName);
	}
	
	UINT LoadValue(LPCTSTR strValueName, UINT nDefValue)
	{
		chASSERT(!m_strFileName.IsEmpty());
		return GetPrivateProfileInt(m_strSectionName, strValueName, nDefValue, m_strFileName);
	}
	BOOL SaveValue(LPCTSTR strValueName, UINT nValue)
	{
		chASSERT(!m_strFileName.IsEmpty());
		_stprintf(m_strData, _T("%lu"), nValue);
		return WritePrivateProfileString(m_strSectionName, strValueName, m_strData, m_strFileName);;
	}
public:
	wIniFile(){}
	virtual ~wIniFile() { }
	wIniFile(LPCTSTR strFileName, LPCTSTR strSectionName)
	{ 
		m_strFileName = strFileName;
		m_strSectionName = strSectionName;
	}
private:
	chString m_strFileName;
	chString m_strSectionName;
	TCHAR m_strData[MAX_PATH];
};

//////////////////////////////////////////////////////////////////////
// wRectWindow class
class wRectWindow : public CRect
{
public:
	wRectWindow (HWND hWnd)
	{
		::GetWindowRect (hWnd, this);
	}
};

//////////////////////////////////////////////////////////////////////
// wRectClient class
class wRectClient : public CRect
{
public:
	wRectClient (HWND hWnd)
	{
		::GetClientRect (hWnd, this);
	}
};
//////////////////////////////////////////////////////////////////////
// wPointCursor class
class wPointCursor : public CPoint
{
public:
	wPointCursor (){GetCursorPos(this);}
};

//////////////////////////////////////////////////////////////////////
// wImageList class
class wImageList : public CImageList  
{
public:
	HBITMAP GetAt(int iIndex)
	{
		IMAGEINFO ImageInfo = {0};
		GetImageInfo(iIndex, &ImageInfo);
		return ImageInfo.hbmImage;
	}
public:
	wImageList( UINT nBitmapID, int cx, int nGrow, COLORREF crMask )
	{
		Create(nBitmapID, cx, nGrow, crMask);
	}
	~wImageList(){}
};

//typedef list<int> HookList;
//typedef ETL_map<HWND, HookList> WindowHookMap;
//class LCHookChainObject : public LCLabledObject<WindowHookMap, 0xFFFFFFFF, (LPCTSTR)_T("NULL")>
//{
//public:
//	static HookList& GetWindowHookList(HWND hWnd)
//	{
//		return Get()[hWnd];
//	}
//};
#pragma pack(push,1)
struct WndProcCell
{
	void Init(WNDPROC proc, void* pHookObject)
	{
		m_mov = 0x042444C7;  //C7 44 24 0C
		m_aHookObject = (DWORD)pHookObject;
		m_jmp = 0xe9;
		m_relproc = (DWORD)proc - ((DWORD)this + sizeof(WndProcCell));
		// write block from data cache and
		//  flush from instruction cache
		FlushInstructionCache(GetCurrentProcess(), this, sizeof(WndProcCell));
	}
	void _Init(WNDPROC proc)
	{
		m_mov = 0x042444C7;  //C7 44 24 0C
		m_aHookObject = (DWORD)0;
		m_jmp = 0xe9;
		m_relproc = (DWORD)proc - ((DWORD)this + 5);
		
		m_mov = m_jmp | m_relproc << 8;
		m_aHookObject = m_relproc >> 24;

		m_relproc = m_jmp = 0;
		// write block from data cache and
		//  flush from instruction cache
		FlushInstructionCache(GetCurrentProcess(), this, sizeof(WndProcCell));
	}
public:
	DWORD   m_mov;          // mov dword ptr [esp+0x4], aHookObject (esp+0x4 is hWnd)
	DWORD   m_aHookObject;  //
	BYTE    m_jmp;          // jmp WndProc
	DWORD   m_relproc;      // relative jmp Self WinProc
};
#pragma pack(pop)

//template <class T>
class wMessageHook
{
protected:
	virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) = 0;
public:
	LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		return ::CallWindowProc(m_pfnOldWindowProc, m_hWnd, uMsg, wParam, lParam);
	}
	
	static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		wMessageHook* pThis = (wMessageHook*)hWnd;
		chASSERT(pThis->m_hWnd != NULL);
		
		LRESULT lRes;
		BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);
		
		// do the default processing if message was not handled
		if(!bRet)
			lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
		
		if(uMsg == WM_NCDESTROY)
			pThis->UnHookWindowMessage();
		return lRes;
	}
	
	BOOL HookWindowMessage(HWND hWnd)
	{
		chASSERT(m_hWnd == NULL);
		chASSERT(::IsWindow(hWnd));
		

		m_pProcCell->Init(wMessageHook::WindowProc, this);
		WNDPROC pProc = (WNDPROC)m_pProcCell;
		
		WNDPROC pfnWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
		if(pfnWndProc == NULL)
			return FALSE;
		m_pfnOldWindowProc = pfnWndProc;
		m_hWnd = hWnd;
		
		// Add to Globle at taget Window HookChain
//			wMessageHook*& pFirstHook = GetProcessNamedValue((LPCTSTR)hWnd, wMessageHook*);
//			m_pNextHook = pFirstHook;
//			pFirstHook = this;
		
		return TRUE;
	}
	
	// Use only if you want to subclass before window is destroyed,
	// WindowProc will automatically subclass when  window goes away
	BOOL UnHookWindowMessage()
	{
		chASSERT(m_hWnd != NULL);
		
//			wMessageHook*& pFirstHook = GetProcessNamedValue((LPCTSTR)m_hWnd, wMessageHook*);
//			if(pFirstHook == this)
//			{// WNDPROC may change by other thread or dll so pOurProc != pActiveProc
//				WNDPROC pOurProc = (WNDPROC)&(m_thunk);
//				WNDPROC pActiveProc = (WNDPROC)::GetWindowLong(m_hWnd, GWL_WNDPROC);
//				if(pOurProc == pActiveProc && !::SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_pfnOldWindowProc))
//					return FALSE;
//				
//				pFirstHook = m_pNextHook;
//			}
//			else
//			{
//				wMessageHook* pPreHook = pFirstHook;
//				for(wMessageHook* pHook = pPreHook->m_pNextHook; pHook != NULL; pHook = pHook->m_pNextHook)
//				{
//					if(pHook == this)
//					{
//						pPreHook->m_pfnOldWindowProc = m_pfnOldWindowProc;
//						break;
//					}
//					pPreHook = pHook;
//				}
//				chASSERT(pHook != NULL);
//			}
//			m_pfnOldWindowProc = NULL;
//			m_hWnd = NULL;
//			m_pNextHook = NULL;
		return TRUE;
	}
public:
	wMessageHook() : m_hWnd(NULL),
		m_pfnOldWindowProc(NULL){}

protected:
	HWND m_hWnd;
	WndProcCell* m_pProcCell;
	WNDPROC m_pfnOldWindowProc;
};

#endif //__wUIKIT_H__
